Mostly Harmless
Legacy:Useful Mutator Functions
Some useful Mutator (UT) functions. The code on this page probably doesn't work at all in UT2003.
Contents
HUD Mutators[edit]
"How do I register a HUD mutator?"
"Why doesn't my HUD mutator work in online/LAN games?"
This is a sample HUD mutator which does nothing more that registering itself and showing how to get a reference to the local PlayerPawn and the HUD (UT).
class MyHUDMutator extends Mutator; var PlayerPawn MyPlayer; var HUD MyHUD; simulated function Tick(float DeltaTime) { if ( !bHUDMutator && Level.NetMode != NM_DedicatedServer ) RegisterHUDMutator(); } simulated function PostRender(canvas Canvas) { MyPlayer = Canvas.Viewport.Actor; if ( MyPlayer != None ) MyHUD = MyPlayer.myHUD; // This is important! It allows the next HUD mutator to draw on the canvas. if ( NextHUDMutator != None ) NextHUDMutator.PostRender(Canvas); } defaultproperties { RemoteRole=ROLE_SimulatedProxy bAlwaysRelevant=True bNetTemporary=True // Set to false if you need to replicate data to the clients more than once per level }
Note: Don't use the code of the Team Beacon HUD mutator or the Relics. Team Beacon is far to complicated for this simple task and the RelicHUDMutator (apart from being incompatible to regular HUD mutators) calls its Destroy function without properly unregistering itself. (see Destructable Mutators below)
Note: If you want to execute code once client-side, but wish to do it after values have been replicated from the server to the client then used the PostNetBeginPlay() function.
Also see Netcode Idioms.
Destructable Mutators[edit]
Never destroy a Mutator (UT) while it still is in any of the mutator chains. This would effectively disconnect all mutators further down in the list. If you really need to destroy a mutator make sure it was unregistered before destroying it.
The other possible solution is that the mutator unregisters itself:
// correctly unregister this mutator from all linked mutator chains simulated function Destroyed() { local Mutator M; local HUD H; if ( Level.Game != None ) { if ( Level.Game.BaseMutator == Self ) Level.Game.BaseMutator = NextMutator; if ( Level.Game.DamageMutator == Self ) Level.Game.DamageMutator = NextDamageMutator; if ( Level.Game.MessageMutator == Self ) Level.Game.MessageMutator = NextMessageMutator; } ForEach AllActors(Class'Engine.HUD', H) if ( H.HUDMutator == Self ) H.HUDMutator = NextHUDMutator; ForEach AllActors(Class'Engine.Mutator', M) { if ( M.NextMutator == Self ) M.NextMutator = NextMutator; if ( M.NextHUDMutator == Self ) M.NextHUDMutator = NextHUDMutator; if ( M.NextDamageMutator == Self ) M.NextDamageMutator = NextDamageMutator; if ( M.NextMessageMutator == Self ) M.NextMessageMutator = NextMessageMutator; } }
Replacing Inventory Items[edit]
The ReplaceWith function does its job in most cases, but not in all maps the items are placed with their default properties. Some have the rotation turned off (either bRotatingPicklup=False, which is the recommended way to do it, or RotationRate.Yaw=0) or float in mid-air though they normally fall down. (Physics=PHYS_None instead of PHYS_Falling)
Also mutator sometimes want to further adjust the properties of the item after spawning it.
The ReplaceWithItem function takes both into account: It modifies the new item's rotation and physics and returns it, so the mutator can modify its properties.
// Modified version of the ReplaceWith function // Replaces an inventory Other with an inventory of class aClassName and // returns a reference to it final function Inventory ReplaceWithItem(Inventory Other, coerce string aClassName) { local Inventory A; local class<Inventory> aClass< SEMI > local bool bAllowItemFall, bForceItemFall; local bool bAllowItemRotation, bForceItemRotation; if ( Other.Location == vect(0,0,0) ) return None; aClass = class<Inventory>(DynamicLoadObject(aClassName, class'Class')); if ( aClass != None ) A = Spawn(aClass, Other.Owner, Other.Tag, Other.Location + (aClass.Default.CollisionHeight - Other.CollisionHeight) * vect(0,0,1), Other.Rotation); if ( Other.MyMarker != None ) { Other.MyMarker.markedItem = A; if ( A != None ) A.MyMarker = Other.MyMarker; Other.MyMarker = None; } else if ( A != None ) { A.bHeldItem = true; A.Respawntime = 0.0; } if ( A != None ) { if ( Other.Physics != Other.Class.Default.Physics ) { if ( Other.Physics == PHYS_Falling ) bForceItemFall = True; else if ( Other.Class.Default.Physics == PHYS_Falling ) bAllowItemFall = False; } if ( (!Other.bRotatingPickup || Other.RotationRate == rot(0,0,0)) && (Other.Rotation.Pitch != 0 || Other.Rotation.Roll != 0) ) bAllowItemRotation = False; else bAllowItemRotation = (Other.RotationRate != rot(0,0,0) && Other.bRotatingPickup) || !Other.default.bRotatingPickup || Other.default.RotationRate == rot(0,0,0); bForceItemRotation = Other.RotationRate != rot(0,0,0) && Other.bRotatingPickup && (!Other.default.bRotatingPickup || Other.default.RotationRate == rot(0,0,0)); if ( A.Physics == PHYS_Falling && !bAllowItemFall ) A.SetPhysics(PHYS_None); else if ( A.Physics != PHYS_Falling && bForceItemFall ) A.SetPhysics(PHYS_Falling); A.bRotatingPickup = bAllowItemRotation && (A.bRotatingPickup || bForceItemRotation); A.Event = Other.Event; A.Tag = Other.Tag; return A; } return None; }
Giving Weapons To Players[edit]
This function is a modified version of the DeathMatchPlus.GiveWeapon function. It adds the weapon to the player's inventory and optionally brings it up as the players selected weapon. The function returns the weapon.
// Give a weapon to a player and optionally bring it up as current weapon. function Weapon GiveWeapon(Pawn PlayerPawn, string aClassName, optional bool bBringUp) { local class<Weapon> WeaponClass< SEMI > local Weapon NewWeapon; WeaponClass = class<Weapon>(DynamicLoadObject(aClassName, class'Class')); if ( PlayerPawn.FindInventoryType(WeaponClass) != None ) return None; newWeapon = Spawn(WeaponClass); if ( newWeapon != None ) { newWeapon.RespawnTime = 0.0; newWeapon.GiveTo(PlayerPawn); newWeapon.bHeldItem = true; newWeapon.GiveAmmo(PlayerPawn); newWeapon.SetSwitchPriority(PlayerPawn); newWeapon.WeaponSet(PlayerPawn); newWeapon.AmbientGlow = 0; if ( PlayerPawn.IsA('PlayerPawn') ) newWeapon.SetHand(PlayerPawn(PlayerPawn).Handedness); else newWeapon.GotoState('Idle'); if ( bBringUp ) { PlayerPawn.Weapon.GotoState('DownWeapon'); PlayerPawn.PendingWeapon = None; PlayerPawn.Weapon = newWeapon; PlayerPawn.Weapon.BringUp(); } } return newWeapon; }
Related Topics[edit]
Discussion[edit]
Kuhal: I have a developed a way to allow a mutator to "appear" to handle Damage to players BEFORE Inventory.ReduceDamage is called. Shall I create a page for this and link to it from OpenSource or would it be better to create the page and link form my homepage until one of the guru's can comment on the content? I investigated this while helping someone on the forums who wanted it specifically for his SpawnProtection code.
Mychaeel: Would you describe that method in a few words? – By the way, I believe the sections on this page would be well worth individual pages; maybe subpages of this one.
Kuhal: Synopsis: Create an Inventory Item with Charge=0; ArmorAbsorption=0; AbsorptionPriority=1000; bIsAnArmor=true;. Override ArmorAbsorbDamage in new class to update member variables for use by the mutator. Make sure that the mutator deals out the damage accordingly because the REAL armor was not damaged in the hit. Has limited usefulness - Why oh why didn't EPIC give Inventory.ReduceDamage the Victim and InstigatedBy vars too? I'm interested in other ideas in this area so by all means hook me up. I have full working source for the SpawnProtection use if needed.
Csimbi: Could someone add an example of adding another mutator from a mutator that is already added? I want to have only one entry in the .int file for practical reasons (the one Mutator would add all the others), and I cannot figure out how to do it. I tried a couple of things, but none of those worked so far. Thank You in advance.
Foxpaw: Depending on what exactly you had in mind, that may be quite easy. Or it may be next to impossible. Do you mean, you only want the one entry in the mutator list and when play begins all the other mutators are added? Or do you mean you want all of them to show up in the mutator list, but only want one listed in the INT file?
Wormbo: Just Level.Game.BaseMutator.AddMutator(Spawn(theMutatorClass)) should do the trick, but why don't you just combine the mutators into a single class?
Csimbi:
Foxpaw, the idea is that one mutator adds all the others.
Wormbo, in which function do You call this? Prebeginplay, PostBeginPlay, or ?
Wormbo: Yes, call it from one of the *BeginPlay functions.
Csimbi: I do this in the Mutator's PostBeginplay: Level.Game.BaseMutator.AddMutator(Spawn(class'CustomTranslocator'));
The error message I get is: "Error, Call to 'AddMutator': type mismatch in parameter 1"
What am I doing wrong?
Wormbo: 'Type mismatch' compiler errors tell you that the function expects a different variable type than you used. This CustomTranslocator class of yours, it's a mutator subclass, isn't it?
Csimbi: My mistake. It's a TournamentWeapon. Changed class name to the mutator's class, and it works like a charm. Thank You.
Vindexus: I tried to make an arena mut using the Replace function above. However, when i try to
compile it with the ucc make command, it says I'm missing a ";" in "Class" on line 4 where the function is started. Anybody know what that means?
EntropicLqd: Check your class definition. It should be similar to the very first line of the HUD Mutator code snippet (first code snippet) on this page. If you class definition is correct then check that you don't mention the word class anywhere in a comment block before the class declaration line. e.g. the following code will give you an error at line 1:
// This class is a mutator that does some stuff class MyMutator extends Mutator; // .. rest of class definition ...
Vindexus: For what it's worth, I'm using UT2004. Here's the line of code that has the error on it
final function Inventory ReplaceWithItem(Inventory Other, coerce string MyWeaponPickup)
EntropicLqd: The line number the error is being reported on is bogus. Something that's pretty normal for compilers. Check your code for missing semi-colons and the like - especially after the class definition. It might be worth posting the whole code block rather than just a single line.
Wormbo: Vindexus, please read the top of this page again:
Some useful Mutator (UT) functions. The code on this page probably doesn't work at all in UT2003.
These functions are not intended to be used in UT2003/4 because many aspects of Mutators and also the Pickup/Inventory system have changed.
AJRAD: I'm trying to use the weapon replacement mutator thingy, but you don't specify which bits to change!!! How the hell is a newbies such as me supposed to understand? Please tell me what needs to be changed.